/*
* BallsOfFuryGameState.java
*
*  Copyright (c) 2008 Corey Oliver and Anshul Kumaria
*
*  This file is part of Balls of Fury.
*
*  Balls of Fury is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 3 of the License, or
*  (at your option) any later version.
*
*  Balls of Fury is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>
*
*
*/

/*
* EDIT:  03/13/2008 - This is an example of how an edit performed to the class
* would appear.
*/
package ballsoffury.game;

import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;
import com.jmex.game.state.BasicGameState;
import com.jme.light.PointLight;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.input.controls.GameControlManager;
import com.jme.math.Vector3f;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.scene.Text;
import com.jme.math.Vector2f;
import com.jme.image.Texture;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;

import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.PhysicsSpace;
import com.jme.scene.shape.Sphere;
import com.jme.scene.shape.Box;
import com.jmex.physics.material.Material;
import com.jmex.physics.geometry.PhysicsBox;
import com.jmex.physics.contact.MutableContactInfo;

/**
* <code>BallsOfFuryGameState</code> allows for management of the game. Various methods
* are included in this class to show various screens, alter preferences,  
* and exit the application.
*
* @author Corey Oliver
* @version $Revision:$ $Date:$
*/
public class BallsOfFuryGameState extends BasicGameState {

   private GameControlManager gameControlManager;
   private PhysicsSpace physicsSpace;
   private Text playerOneScoreGeometryNode;
   private Text playerTwoScoreGeometryNode;
   private DynamicPhysicsNode pingPongBallPhysicsNode;
   private StaticPhysicsNode pingPongPaddlePhysicsNode;

   public BallsOfFuryGameState() {
       super("ballsoffury");
   }

   @Override
   public void setActive(boolean active) {
       super.setActive(active);
       init();

   }

   private void init() {

       Vector2f centerPosition = new Vector2f(DisplaySystem.getDisplaySystem().getWidth() / 2,
               DisplaySystem.getDisplaySystem().getHeight() / 2);
       
       // Create a <code>PhysicsSpace</code>
       physicsSpace = PhysicsSpace.create();

       // Create a <code>StaticPhysicsNode</code> that the ping pong table
       // can attach to
       StaticPhysicsNode pingPongTablePhysicsNode = physicsSpace.createStaticNode();
       rootNode.attachChild(pingPongTablePhysicsNode);
       pingPongTablePhysicsNode.setMaterial(Material.WOOD);
       
       // Because the model throws an error when
       // <code>generatePhysicsGeometry()</code> is run a box model needs to
       // be created to take its place
       pingPongTablePhysicsNode.createBox("tablePhysics");
       pingPongTablePhysicsNode.setLocalScale(new Vector3f(75, 2, 250));
       pingPongTablePhysicsNode.setLocalTranslation(0, -17, -130);
       
       // Create a physics for the net
       StaticPhysicsNode pingPongNetPhysicsNode = physicsSpace.createStaticNode();
       rootNode.attachChild(pingPongNetPhysicsNode);
       pingPongNetPhysicsNode.setMaterial(Material.WOOD);
       
       pingPongNetPhysicsNode.createBox("netPhysics");
       pingPongNetPhysicsNode.setLocalScale(new Vector3f(80, 7, 2));
       pingPongNetPhysicsNode.setLocalTranslation(0, -15, -135);
       
       pingPongNetPhysicsNode.generatePhysicsGeometry();

       // Load and attach the pingpong table to a <code>Node</code>
       Node pingPongTableGeometryNode = ObjLoader.load("ballsoffury/models/pingpong.obj");
       rootNode.attachChild(pingPongTableGeometryNode);
       pingPongTableGeometryNode.setLocalTranslation(-75, -100, -10);
       
       // Load textures for the model
       TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
       Texture t = TextureManager.loadTexture(BasicGameState.class.getClassLoader().getResource(
       "ballsoffury/models/paddle/_Wood_floor_light_.jpg"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
       ts.setTexture(t);
       pingPongTableGeometryNode.setRenderState(ts);
       
       // Generate geometry
       pingPongTablePhysicsNode.generatePhysicsGeometry();

       // Create a <code>DynamicPhysicsNode</code> that the pingpong ball can
       // attach to
       pingPongBallPhysicsNode = physicsSpace.createDynamicNode();
       rootNode.attachChild(pingPongBallPhysicsNode);
       
       /**
       // Create custom material
        final Material PLASTIC = new Material("plastic");
        // we make it really light
        PLASTIC.setDensity( 0.05f );
        // a material should define contact detail pairs for each other material it could collide with in the scene
        // do that just for the floor material - the DEFAULT material
        MutableContactInfo contactDetails = new MutableContactInfo();
        // our material should not bounce on DEFAULT
        contactDetails.setBounce(10);
        // and should never slide on DEFAULT
        contactDetails.setMu( 10 ); // todo: Float.POSITIVE_INFINITY seems to cause issues on Linux (only o_O)
        // now set that behaviour
        PLASTIC.putContactHandlingDetails( Material.DEFAULT, contactDetails );
        */

       pingPongBallPhysicsNode.setMaterial(Material.RUBBER);
       
       // Create a <code>Sphere</code> to attach to the
       // <code>DynamicPhysicsNode</code>
       Sphere pingPongBallGeometryNode = new Sphere("pingpong", 100, 100, 1);
       pingPongBallPhysicsNode.attachChild(pingPongBallGeometryNode);
       pingPongBallPhysicsNode.setLocalTranslation(0, 5, -100);
       
       // Generate geometry
       pingPongBallPhysicsNode.generatePhysicsGeometry();

       // Create a <code>StaticPhysicsNode</code> that the ping pong paddle
       // can attach to
       pingPongPaddlePhysicsNode = physicsSpace.createStaticNode();
       rootNode.attachChild(pingPongPaddlePhysicsNode);
       pingPongPaddlePhysicsNode.setMaterial(Material.WOOD);
       
       // Load and attach the pingpong paddle to a <code>Node</code>
       Node pingPongPaddleGeometryNode = ObjLoader.load("ballsoffury/models/ping.obj");
       pingPongPaddlePhysicsNode.attachChild(pingPongPaddleGeometryNode);
      pingPongPaddlePhysicsNode.setLocalTranslation(centerPosition.x,
               -15, 0);
       pingPongPaddlePhysicsNode.setLocalRotation(new Quaternion(new float[]{90 * FastMath.DEG_TO_RAD, 0, 0}));
       
       // Generate geometry
       pingPongPaddlePhysicsNode.generatePhysicsGeometry();

       // Create scoring <code>Nodes</code>
       // Player one
        playerOneScoreGeometryNode = Text.createDefaultTextLabel("Score", "0");
        rootNode.attachChild(playerOneScoreGeometryNode);
        playerOneScoreGeometryNode.setLocalTranslation(520, 450, 0);
        // Test if <code>score</code> syncs with <code>ScoreManager</code>
        ScoreManager.incrementPlayerOneScore();
        
        // Player two
        playerTwoScoreGeometryNode = Text.createDefaultTextLabel("Score", "0");
        rootNode.attachChild(playerTwoScoreGeometryNode);
        playerTwoScoreGeometryNode.setLocalTranslation(550, 450, 0);
        // Test if <code>score</code> syncs with <code>ScoreManager</code>
        ScoreManager.incrementPlayerTwoScore();
        ScoreManager.incrementPlayerTwoScore();

       PointLight pl = new PointLight();
       pl.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1));
       pl.setDiffuse(new ColorRGBA(1, 1, 1, 1));
       pl.setLocation(new Vector3f(10, -50, 20));
       pl.setEnabled(true);

       rootNode.updateRenderState();

       addControllers();
       initLight();

   }

   public void resetPingPongBall() {
       pingPongBallPhysicsNode.clearDynamics();
       pingPongBallPhysicsNode.setLocalTranslation(0, 5, -100);
   }
   
   private void updateScores() {
        
        // Player one
        if (!playerOneScoreGeometryNode.getText().toString().equals(String.valueOf(ScoreManager.getPlayerOneScore()))) {
            rootNode.detachChild(playerOneScoreGeometryNode);
            playerOneScoreGeometryNode = Text.createDefaultTextLabel("Score", String.valueOf(ScoreManager.getPlayerOneScore()));
            rootNode.attachChild(playerOneScoreGeometryNode);
            playerOneScoreGeometryNode.setLocalTranslation(520, 450, 0);
        }
        
        // Player two
        if (!playerTwoScoreGeometryNode.getText().toString().equals(String.valueOf(ScoreManager.getPlayerTwoScore()))) {
            rootNode.detachChild(playerTwoScoreGeometryNode);
            playerTwoScoreGeometryNode = Text.createDefaultTextLabel("Score", String.valueOf(ScoreManager.getPlayerTwoScore()));
            rootNode.attachChild(playerTwoScoreGeometryNode);
            playerTwoScoreGeometryNode.setLocalTranslation(550, 450, 0);
        }
    }

   private void initLight() {
       PointLight light = new PointLight();
       light.setDiffuse(new ColorRGBA(0.55F, 0.55F, 0.55F, 1.0F));
       light.setAmbient(new ColorRGBA(0.05F, 0.05F, 0.05F, 1.0F));
       light.setLocation(new Vector3f(100, 100, 100));
       light.setEnabled(true);

       // Attach the light to a lightState and the lightState to rootNode
       LightState lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState();
       lightState.setEnabled(true);
       lightState.attach(light);
       rootNode.setRenderState(lightState);
   }

   public void addControllers() {
       gameControlManager = new GameControlManager();
       rootNode.addController(new PlayerController(this, pingPongPaddlePhysicsNode, 0.5f, 0.5f));
       rootNode.addController(new BackToMenuController(this));
       rootNode.addController(new ResetPingPongBallController(this, pingPongPaddlePhysicsNode));
   }

   public GameControlManager getGameControlManager() {
       return gameControlManager;
   }

   @Override
   public Node getRootNode() {
       return rootNode;
   }

   @Override
   public final void update(float tpf) {


       super.update(tpf);
       rootNode.updateGeometricState(tpf, true);
       physicsSpace.update(.05f);
       
       // Update scores
        updateScores();
        
       // Test if ball has fallen off table
       if ( pingPongBallPhysicsNode.getLocalTranslation().y < -50 ) {
           resetPingPongBall();
       }
   }
}
